

import gtk
import pygtk
import gobject

import copy
import threading

from . import UiProgress
import resources
from ..balancer import BalanceSource, BalancerLoginException

class PassphraseEntryDialog:
    """Responsible for consuming the password entry."""

    def run(self, passphrase_cb, reset_cb):
        self.__passphrase_cb = passphrase_cb
        self.__reset_cb = reset_cb

        # Create the window
        self.dialog = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION, message_format="Enter the passphrase used to secure your passwords.")
        self.dialog.set_modal(True)
        self.dialog.set_title("Enter passphrase")
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_DIALOG)
        self.dialog.set_image(image)
        image.show()
        self.dialog.set_icon_list(*resources.icon_list(resources.BASE_ICON))

        # Add the dialogy goo
        self.__entry = gtk.Entry()
        self.__entry.set_visibility(False)
        self.__entry.set_invisible_char(u"\u2022")
        self.__entry.grab_focus()
        self.__entry.set_activates_default(True)

        self.dialog.vbox.pack_end(self.__entry)
        self.__entry.show()

        # Add the reset button
        reset = gtk.Button(label="_Reset Passwords", use_underline=True)
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_CLEAR, gtk.ICON_SIZE_BUTTON)
        reset.set_image(image)
        image.show()
        self.dialog.add_action_widget(reset, gtk.RESPONSE_DELETE_EVENT)
        reset.show()

        # Add the ok button
        self.dialog.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_OK)
        self.dialog.set_default_response(gtk.RESPONSE_OK)

        # Set up the signals
        self.dialog.connect("response", self.__signal_response)

        self.dialog.show()

    def __signal_response(self, dialog, response_id):
        """The user is done. """
        if response_id == gtk.RESPONSE_OK:
            self.__passphrase_cb(self.__entry.get_text())
            self.dialog.destroy()
        elif response_id == gtk.RESPONSE_DELETE_EVENT:
            d = PassphraseResetDialog()

            def my_reset_cb():
                dialog.destroy()
                self.__reset_cb()

            d.run(my_reset_cb)
        else:
            print "Unknown response " + response_id
            

class PassphraseResetDialog:
    """Ask the user if they want to trash their keyring."""

    def run(self, yes_cb):
        self.__yes_cb = yes_cb

        # Create the window
        self.dialog = gtk.MessageDialog(
                type=gtk.MESSAGE_QUESTION, 
                message_format="Permanently delete all of your credentials?",
                buttons=gtk.BUTTONS_YES_NO)
        self.dialog.set_modal(True)
        self.dialog.set_title("Delete stored credentials?")
        self.dialog.set_icon_list(*resources.icon_list(resources.BASE_ICON))

        # Set up the signals
        self.dialog.connect("response", self.__signal_response)

        self.dialog.show()

    def __signal_response(self, dialog, response_id):
        """The user is done. """
        if response_id == gtk.RESPONSE_YES:
            self.__yes_cb()
        elif response_id == gtk.RESPONSE_NO:
            pass
        else:
            print "Unknown response id " + response_id
            
        self.dialog.destroy()


class StartupWizard:
    """Wizard prompting users for their initial password."""

    def __init__(self):
        self.__passphrase = None

    def run(self, passphrase_cb, done_cb, cancel_cb):
        """Display the wizard. Calls the appropriate callback when the user is done."""
        self.__passphrase_cb = passphrase_cb
        self.__done_cb = done_cb
        self.__cancel_cb = cancel_cb

        w = self.__wizard = gtk.Assistant()
        w.set_position(gtk.WIN_POS_CENTER)
        w.set_icon_list(*resources.icon_list(resources.BASE_ICON))

        self.__add_page_intro(0)
        self.__add_page_done(1)

        w.connect("prepare", self.__prepare)
        w.connect("cancel", self.__cancel)
        w.connect("close", self.__close)

        w.show_all()


    def __cancel(self, assistant):
        self.__cancel_cb()
        assistant.destroy()

    def __close(self, assistant):
        self.__done_cb()
        assistant.destroy()


    def __prepare(self, w, page):
        if page == self.__page_done:
            self.__passphrase_cb()
            self.__wizard.set_page_complete(page, True)


    def __add_page_intro(self, index):
        """Add an introduction page that tells the user what's going on."""
        self.__page_intro = page = gtk.VBox()

        label = gtk.Label()
        label.set_markup("Balancer checks your online balances and notifies an email address of your choice.\n\n<b>Warning:</b> Balancer stores passwords on your computer. <i>This is inherently dangerous.</i> An attacker may use this application to get access to your online accounts. By using this program you absolve the author of all responsibility for security breaches and other problems.")
        label.set_line_wrap(True)
        page.pack_start(label, expand=False)

        w = self.__wizard
        w.insert_page(page, index)
        w.set_page_title(page, "Welcome to Balancer")
        w.set_page_type(page, gtk.ASSISTANT_PAGE_INTRO)
        w.set_page_header_image(page, resources.icon(resources.BASE_ICON, 32))

        w.set_page_complete(page, True)


    def __add_page_done(self, index):
        """Add a summary page telling the user that they're done."""
        self.__page_done = page = gtk.VBox()

        label = gtk.Label()
        label.set_markup("Balancer is configured. \n\nAdd accounts with the Balancer applet in your notification area.")
        label.set_line_wrap(True)
        page.pack_start(label, expand=False)

        w = self.__wizard
        w.insert_page(page, index)
        w.set_page_title(page, "Balancer Configured")
        w.set_page_type(page, gtk.ASSISTANT_PAGE_SUMMARY)
        w.set_page_header_image(page, resources.icon(resources.BASE_ICON, 32))

        w.set_page_complete(page, True)



class AddAccountWizard:
    """Wizard allowing users to add an account"""

    def __init__(self, gm, account):
        self.gm = gm
        self.__account = account

        self.__page_editor = None

    def run(self, save_cb):
        """Display the wizard"""
        self.__save_cb = save_cb

        w = self.__wizard = gtk.Assistant()
        w.set_position(gtk.WIN_POS_CENTER)
        w.set_icon_list(*resources.icon_list(resources.BASE_ICON))

        self.__add_page_start(0)
        self.__add_page_name(1)
        self.__editor_index = 2
        self.__add_page_validate(3)
        self.__add_page_confirm(4)

        w.connect("prepare", self.__prepare)
        w.connect("close", self.__done)
        w.connect("cancel", lambda widget: w.destroy())

        w.show_all()

    def __prepare(self, w, page):
        if page == self.__page_name:
            # Create the editor for the account credentials
            w.set_page_complete(page, True)
            iter = self.__types_tree.get_selection().get_selected()[1]
            type = self.__types_tree.get_model().get_value(iter, 2)
            self.__add_edit_page(type, self.__editor_index)
            # Record the type for later use
            self.__account.set_type(type)
            # Set a default name from the account type
            self.__page_name_entry.set_text(type.get_name())
        elif page == self.__page_validation:
            self.__editor.done()
            threading.Thread(target=self.__validate, args=(copy.copy(self.__account), self.__progressWidget,)).start() 

    def __done(self, w):
        """Finalize"""
        self.__save_cb(self.__account)
        w.destroy()

    def __add_page_start(self, index):
        """Add the initial chooser page to the assistant."""
        page = gtk.VBox()

        label = gtk.Label("Choose the type of account to add.")
        label.set_line_wrap(True)
        page.pack_start(label, expand=False)

        types = gtk.ListStore(str, str, object)
        for type in self.gm.get_balancers():
            types.append(["icon", type.get_name(), type])

        tree = self.__types_tree = gtk.TreeView(types)
        tree.set_headers_visible(False)
    
        w = self.__wizard
        def cb(selection): 
            iter = selection.get_selected()[1]
            w.set_page_complete(page, iter != None)
                
        tree.get_selection().connect("changed", cb)

        col = gtk.TreeViewColumn()
        tree.append_column(col)
        render = gtk.CellRendererText()
        col.pack_start(render, True)
        col.add_attribute(render, 'text', 1)
        page.pack_start(tree, expand=True, fill=True)

        w.insert_page(page, index)
        w.set_page_title(page, "Add an Account")
        w.set_page_type(page, gtk.ASSISTANT_PAGE_INTRO)
        w.set_page_header_image(page, resources.icon(resources.BASE_ICON, 32))

        
    def __add_page_name(self, index):
        """Add the page that asks the user to name the new account."""
        page = self.__page_name = gtk.VBox()

        label = gtk.Label("Choose the name for this account. The name can be changed later.")
        label.set_line_wrap(True)
        page.pack_start(label, expand=False)

        ask = gtk.HBox()
        label = gtk.Label("Account name:")
        ask.pack_start(label, expand=False)

        self.__page_name_entry = entry = gtk.Entry()
        ask.pack_start(entry)

        page.pack_start(ask)

        w = self.__wizard
        cb = lambda e: w.set_page_complete(page, entry.get_text_length() > 0)
        entry.connect("changed", cb)

        w.insert_page(page, index)
        w.set_page_title(page, "Set Account Name")
        w.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT)
        w.set_page_header_image(page, resources.icon(resources.BASE_ICON, 32))


    def __add_page_validate(self, index):
        """Add a page that validates the account info"""
        page = gtk.VBox()

        label = gtk.Label("Verifying account information...")
        label.set_line_wrap(True)
        page.pack_start(label, expand=False)
        
        prog = self.__progressWidget = gtk.ProgressBar()
        page.pack_start(prog, fill=False)

        self.__page_validation = page

        w = self.__wizard
        w.insert_page(page, index)
        w.set_page_title(page, "Validate Account")
        w.set_page_type(page, gtk.ASSISTANT_PAGE_PROGRESS)
        w.set_page_header_image(page, resources.icon(resources.BASE_ICON, 32))


    def __validate(self, account, progressWidget):
        """Validate the account. Run outside of the UI thread."""
        n = None
        try:
            self.__editor.get_type().load(account, 
                    UiProgress(progressWidget), rate=BalanceSource.FAST)
            def n():
                self.__wizard.set_page_complete(self.__page_validation, True)
                self.__wizard.set_current_page(4)

        except BalancerLoginException as e:
            def n():
                self.__wizard.set_page_complete(self.__page_validation, False)
                self.__wizard.set_current_page(self.__editor_index)
                self.__editor.set_validation_exception(e)


        gobject.idle_add(n)

    
    def __add_edit_page(self, type, index):
        """Create the edit page for the given type."""
        self.__page_editor = page = gtk.VBox()

        label = gtk.Label("Edit the account information. This information will be encoded when it is saved. You won't be able to modify it in future. ")
        label.set_line_wrap(True)
        page.pack_start(label, expand=False)

        w = self.__wizard
        complete = lambda complete: w.set_page_complete(page, complete)
        self.__editor = self.gm.get_editor_for(type, complete, self.__account)
        page.pack_start(self.__editor.widget())
        
        w.insert_page(page, index)
        w.set_page_title(page, "Set Account Information")
        w.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT)
        w.set_page_header_image(page, resources.icon(resources.BASE_ICON, 32))

        # The following is necessary to get the assitant to show our newly (dynamically) added page
        w.show_all()


    def __add_page_confirm(self, index):
        """Add the initial chooser page to the assistant."""
        page = gtk.VBox()

        label = gtk.Label("Account successfully created and validated.")
        label.set_line_wrap(True)
        page.pack_start(label, expand=False)

        w = self.__wizard
        w.insert_page(page, index)
        w.set_page_title(page, "Account Created")
        w.set_page_type(page, gtk.ASSISTANT_PAGE_SUMMARY)
        w.set_page_header_image(page, resources.icon(resources.BASE_ICON, 32))
